﻿using huqiang.core.Data;
using huqiang.Data;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace huqiang.Core.HGUI
{
    public struct ArrayInfo
    {
        public int Start;
        public int Length;
    }
    internal class BatchBuffer
    {
        public HGUIElement[] PipeLine=new HGUIElement[2048];
        internal List<Vector3> vertex = new List<Vector3>();
        internal List<Vector2> uv = new List<Vector2>();
        /// <summary>
        /// picture index
        /// </summary>
        internal List<Vector2> uv1 = new List<Vector2>();
        /// <summary>
        /// cut rect
        /// </summary>
        internal List<Vector2> uv2 = new List<Vector2>();
        /// <summary>
        /// uv tiling
        /// </summary>
        internal List<Vector2> uv3 = new List<Vector2>();
        /// <summary>
        /// uv offset
        /// </summary>
        internal List<Vector2> uv4 = new List<Vector2>();
        internal List<Color32> colors = new List<Color32>();
        internal MaterialCollector MatCollector = new MaterialCollector();
    }
    internal class BatchBuffer2
    {
        internal List<Vector3> vertex = new List<Vector3>();
        internal List<Vector2> uv = new List<Vector2>();
        /// <summary>
        /// picture index
        /// </summary>
        internal List<Vector2> uv1 = new List<Vector2>();
        /// <summary>
        /// cut rect
        /// </summary>
        internal List<Vector2> uv2 = new List<Vector2>();
        /// <summary>
        /// uv tiling
        /// </summary>
        internal List<Vector2> uv3 = new List<Vector2>();
        /// <summary>
        /// uv offset
        /// </summary>
        internal List<Vector2> uv4 = new List<Vector2>();
        internal List<Color32> colors = new List<Color32>();
        internal List<int> tris = new List<int>(4096);
        internal MaterialBlock MatBlock = new MaterialBlock();
        internal Mesh mesh;
        public void Clear()
        {
            vertex.Clear();
            tris.Clear();
            uv.Clear();
            uv1.Clear();
            uv2.Clear();
            uv3.Clear();
            uv4.Clear();
            colors.Clear();
            MatBlock.Reset();
        }
        public void Apply()
        {
            if (mesh == null)
            {
                mesh = new Mesh();
            }
            mesh.Clear();
            mesh.SetVertices(vertex);
            mesh.SetUVs(0, uv);
            mesh.SetUVs(1, uv1);
            mesh.SetUVs(2, uv2);
            mesh.SetUVs(3, uv3);
            mesh.SetUVs(4, uv4);
            mesh.SetColors(colors);
            mesh.SetTriangles(tris, 0);
        }
    }
    internal class Batchs
    {
        internal int top = 0;
        public void Reset()
        {
            top = 0;
        }
        public List<BatchBuffer2> batches = new List<BatchBuffer2>();
        public BatchBuffer2 GetNextBatch()
        {
            if (top >= batches.Count)
            {
                BatchBuffer2 batch = new BatchBuffer2();
                batches.Add(batch);
                top++;
                return batch;
            }
            var bat = batches[top];
            top++;
            return bat;
        }
    }
    /// <summary>
    /// HGUI图像合批处理
    /// </summary>
    internal class HBatch
    {
        static Texture[] texBuffer = new Texture[4];
        static int[] IDBuffer = new int[4];
        static Vector2[] UV1 = new Vector2[4];
        static Batchs batchs;
        static BatchBuffer2 GetNewBuffer()
        {
            var bat = batchs.GetNextBatch();
            bat.Clear();
            return bat;
        }
        public static void Batch(HGUIElement[] pipeLine, Batchs bats)
        {
            batchs = bats;
            batchs.Reset();
            HGUIElement root = pipeLine[0];
            if (root.script != null)
            {
                int c = root.childCount;
                int os = root.childOffset;
                float hw = UISystem.pixelWidth * 0.5f;//canvas.m_sizeDelta.x * 0.5f;
                float hh = UISystem.pixelHeight * 0.5f;//canvas.m_sizeDelta.y * 0.5f;
                BatchBuffer2 buffer = GetNewBuffer();
                for (int i = 0; i < c; i++)
                {
                    if (pipeLine[os].active)
                    {
                        Batch(pipeLine, os, buffer, new Vector4(-hw, -hh, hw, hh), Vector2.zero, new Vector2(hw, hh));
                    }
                    os++;
                }
            }
        }
        static void Batch(HGUIElement[] pipeLine, int index, BatchBuffer2 buffer, Vector4 clip, Vector2 center, Vector2 half)
        {
            if (!pipeLine[index].active)
                return;

            Vector3 o = pipeLine[index].Position;
            Vector3 scale = pipeLine[index].Scale;
            Quaternion q = pipeLine[index].Rotation;

            bool mask = false;
            var script = pipeLine[index].script;
            if (script != null)
            {
                mask = script.Mask;
                if (mask)//计算遮挡区域
                {
                    float x = script.SizeDelta.x;
                    float y = script.SizeDelta.y;
                    float px = script.Pivot.x;
                    float py = script.Pivot.y;
                    float lx = x * -px;
                    float rx = x + lx;
                    float dy = y * -py;
                    float ty = y + dy;
                    lx *= scale.x;
                    rx *= scale.x;
                    dy *= scale.y;
                    ty *= scale.y;
                    Vector4 v = new Vector4(o.x + lx, o.y + dy, o.x + rx, o.y + ty);

                    clip = CutRect(clip, v);
                    half.x = (clip.z - clip.x) * 0.5f;
                    half.y = (clip.w - clip.y) * 0.5f;
                    if (half.x == 0 | half.y == 0)
                    {
                        return;
                    }
                    center.x = half.x + clip.x;
                    center.y = half.y + clip.y;
                }
                var graphics = script as HGraphics;
                if (graphics != null)//计算图形
                {
                    texBuffer[0] = graphics.MainTexture;
                    texBuffer[1] = graphics.STexture;
                    texBuffer[2] = graphics.TTexture;
                    texBuffer[3] = graphics.FTexture;
                    if(!buffer.MatBlock.CombinationTexture(texBuffer, IDBuffer, 4))
                    {
                        buffer = GetNewBuffer();
                        buffer.MatBlock.CombinationTexture(texBuffer, IDBuffer, 4);
                    }
                    if (graphics.Shadow)
                        AddShadow(graphics, buffer, ref q, ref scale, ref o, ref clip, ref center, ref half);
                    var vs = buffer.vertex;
                    var vc = vs.Count;
                    int dc = graphics.mesh.vertInfo.DataCount;
                    if (dc > 0)
                    {
                        float px = (0.5f - script.Pivot.x) * script.m_sizeDelta.x;
                        float py = (0.5f - script.Pivot.y) * script.m_sizeDelta.y;
                        Vector2 uv2 = Vector2.zero;
                        unsafe
                        {
                            HVertex* hv = graphics.mesh.vertInfo.Addr;
                            for (int j = 0; j < dc; j++)
                            {
                                var tp = hv[j].position;//局部顶点
                                tp.z = 0;
                                tp.x += px;
                                tp.y += py;
                                tp.x *= scale.x;
                                tp.y *= scale.y;
                                var t = q * tp;
                                t += o;
                                t.z = 0;
                                uv2.x = (t.x - center.x) / half.x * 0.001f + 0.5f;
                                uv2.y = (t.y - center.y) / half.y * 0.001f + 0.5f;
                                vs.Add(t);
                                buffer.colors.Add(hv[j].color);
                                buffer.uv.Add(hv[j].uv);
                                buffer.uv2.Add(uv2);
                                buffer.uv3.Add(hv[j].uv3);
                                buffer.uv4.Add(hv[j].uv4);
                            }
                        }
                        AddTris(graphics, buffer, vc, dc);
                    }
                }
            }

            int c = pipeLine[index].childCount;
            int os = pipeLine[index].childOffset;
            for (int i = 0; i < c; i++)
            {
                Batch(pipeLine, os, buffer, clip, center, half);
                os++;
            }
        }
        static void AddTris(HGraphics graphics, BatchBuffer2 buffer, int vc, int dc,bool shadow=false)
        {
            int max = vc + dc;
            if (graphics.mesh.tris != null && graphics.mesh.tris.Length > 0)
            {
                int tid = IDBuffer[0];
                var src = graphics.mesh.tris;
                int len = src.Length;
                if (len > 0)
                {
                    for (int k = 0; k < len; k++)
                    {
                        int index = src[k] + vc;
                        buffer.tris.Add(index);
                        if (index >= max)
                            UnityEngine.Debug.LogError("数组超标");
                    }
                }
                if(shadow)
                    AddShadowUV1(buffer, tid, ref graphics.mesh.vertInfo);
                else
                AddUV1(buffer, tid, ref graphics.mesh.vertInfo);
            }
            else
            {
                ref Triangle4 tris = ref graphics.mesh.triangle;
                for (int i = 0; i < 3; i++)
                {
                    int tc = tris[i].DataCount;
                    if (tc > 0)
                    {
                        unsafe
                        {
                            int* ip = tris[i].Addr;
                            for (int k = 0; k < tc; k++)
                            {
                                buffer.tris.Add(ip[k] + vc);
                            }
                        }
                    }
                }
                unsafe
                {
                    HVertex* hv = graphics.mesh.vertInfo.Addr;
                    if(shadow)
                        AddShadowUV1(buffer, IDBuffer, hv, dc, 3);
                    else AddUV1(buffer, IDBuffer, hv, dc, 3);
                }
            }
            //else
            //{
            //    AddUV1(buffer, 0, ref graphics.mesh.vertInfo);
            //}
        }
        /// <summary>
        /// 添加阴影
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="buffer"></param>
        /// <param name="q"></param>
        /// <param name="scale"></param>
        /// <param name="o"></param>
        /// <param name="clip"></param>
        static void AddShadow(HGraphics graphics, BatchBuffer2 buffer, ref Quaternion q, ref Vector3 scale, ref Vector3 o, ref Vector4 clip, ref Vector2 center, ref Vector2 half)
        {
            var vs = buffer.vertex;
            var vc = vs.Count;
            int dc = graphics.mesh.vertInfo.DataCount;
            var os = graphics.shadowOffsset;
            if (dc > 0)
            {
                float px = (0.5f - graphics.Pivot.x) * graphics.SizeDelta.x + os.x;
                float py = (0.5f - graphics.Pivot.y) * graphics.SizeDelta.y + os.y;
                Vector2 uv2 = Vector2.zero;
                unsafe
                {
                    HVertex* vert = graphics.mesh.vertInfo.Addr;
                    for (int j = 0; j < dc; j++)
                    {
                        var tp = vert[j].position;//局部顶点
                        tp.x += px;
                        tp.y += py;
                        var t = q * tp;
                        t.x *= scale.x;
                        t.y *= scale.y;
                        t += o;
                        t.z = 0;
                        uv2.x = (t.x - center.x) / half.x * 0.001f + 0.5f;
                        uv2.y = (t.y - center.y) / half.y * 0.001f + 0.5f;
                        vs.Add(t);
                        buffer.colors.Add(graphics.shadowColor);
                        buffer.uv.Add(vert[j].uv);
                        buffer.uv2.Add(uv2);
                        buffer.uv3.Add(vert[j].uv3);
                        buffer.uv4.Add(vert[j].uv4);
                    }
                }
                AddTris(graphics, buffer, vc, dc, true);
            }
        }
        /// <summary>
        /// 添加UV
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="tid"></param>
        /// <param name="vertCount"></param>
        static void AddUV1(BatchBuffer2 buffer, int tid, ref MarshalBlock<HVertex> dat)
        {
            Vector2 v = Vector2.zero;
            v.x = tid;
            v.x *= 0.1f;
            var dc = dat.DataCount;
            unsafe
            {
                var hv = dat.Addr;
                for (int i = 0; i < dc; i++)
                {
                    v.y = hv[i].fillColor;
                    buffer.uv1.Add(v);
                }
            }
        }
        static void AddShadowUV1(BatchBuffer2 buffer, int tid, ref MarshalBlock<HVertex> dat)
        {
            Vector2 v = Vector2.zero;
            v.x = tid;
            v.x *= 0.1f;
            var dc = dat.DataCount;
            for (int i = 0; i < dc; i++)
            {
                buffer.uv1.Add(v);
            }
        }
        unsafe static void AddUV1(BatchBuffer2 buffer, int[] ids, HVertex* vertices, int vc, int l)
        {
            for (int i = 0; i < l; i++)
            {
                float x = ids[i];
                UV1[i].x = x * 0.1f;
            }
            for (int i = 0; i < vc; i++)
            {
                int p = vertices[i].picture;
#if DEBUG
                if (p < 0 | p > 3)
                {
                    UnityEngine.Debug.LogError("out of index");
                    p = 0;
                }
#endif
                UV1[p].y = vertices[i].fillColor;
                buffer.uv1.Add(UV1[p]);
            }
        }
        unsafe static void AddShadowUV1(BatchBuffer2 buffer, int[] ids, HVertex* vertices, int vc, int l)
        {
            for (int i = 0; i < l; i++)
            {
                float x = ids[i];
                UV1[i].x = x * 0.1f;
                UV1[i].y = 0;
            }
            for (int i = 0; i < vc; i++)
            {
                int p = vertices[i].picture;
#if DEBUG
                if (p < 0 | p > 3)
                {
                    UnityEngine.Debug.LogError("out of index");
                    p = 0;
                }
#endif
                buffer.uv1.Add(UV1[p]);
            }
        }
        /// <summary>
        /// 矩形遮罩
        /// </summary>
        /// <param name="v0"></param>
        /// <param name="v1"></param>
        /// <returns></returns>
        static Vector4 CutRect(Vector4 v0, Vector4 v1)
        {
            if (v0.x < v1.x)
                v0.x = v1.x;
            if (v0.y < v1.y)
                v0.y = v1.y;
            if (v0.z > v1.z)
                v0.z = v1.z;
            if (v0.w > v1.w)
                v0.w = v1.w;
            if (v0.x > v0.z)
            {
                v0.x = 0;
                v0.z = 0;
            }
            if (v0.y > v0.w)
            {
                v0.y = 0;
                v0.w = 0;
            }
            return v0;
        }
    }
    internal class HBatch2
    {
        static int[] TriBuffer = new int[65536];
        static int[] IDBuffer = new int[32];
        static ArrayInfo[] Arrays = new ArrayInfo[8];
        /// <summary>
        /// 合批处理
        /// </summary>
        /// <param name="buffer">画布实例</param>
        public static void Batch(BatchBuffer buffer)
        {
            buffer.vertex.Clear();
            buffer.uv.Clear();
            buffer.uv1.Clear();
            buffer.uv2.Clear();
            buffer.uv3.Clear();
            buffer.uv4.Clear();
            buffer.colors.Clear();
            HGUIElement[] pipeLine = buffer.PipeLine;
            HGUIElement root = pipeLine[0];
            if (root.script != null)
            {
                buffer.MatCollector.Start();
                int c = root.childCount;
                int os = root.childOffset;
                float hw = UISystem.pixelWidth * 0.5f;//canvas.m_sizeDelta.x * 0.5f;
                float hh = UISystem.pixelHeight * 0.5f;//canvas.m_sizeDelta.y * 0.5f;
                for (int i = 0; i < c; i++)
                {
                    if (pipeLine[os].active)
                    {
                        Batch(pipeLine, os, buffer, new Vector4(-hw, -hh, hw, hh), Vector2.zero, new Vector2(hw, hh));
                    }
                    os++;
                }
                buffer.MatCollector.End();
            }
        }
        static void Batch(HGUIElement[] pipeLine, int index, BatchBuffer buffer, Vector4 clip, Vector2 center, Vector2 half)
        {
            if (!pipeLine[index].active)
                return;

            Vector3 o = pipeLine[index].Position;
            Vector3 scale = pipeLine[index].Scale;
            Quaternion q = pipeLine[index].Rotation;

            bool mask = false;
            var script = pipeLine[index].script;
            if (script != null)
            {
                mask = script.Mask;
                if (mask)//计算遮挡区域
                {
                    float x = script.SizeDelta.x;
                    float y = script.SizeDelta.y;
                    float px = script.Pivot.x;
                    float py = script.Pivot.y;
                    float lx = x * -px;
                    float rx = x + lx;
                    float dy = y * -py;
                    float ty = y + dy;
                    lx *= scale.x;
                    rx *= scale.x;
                    dy *= scale.y;
                    ty *= scale.y;
                    Vector4 v = new Vector4(o.x + lx, o.y + dy, o.x + rx, o.y + ty);

                    clip = CutRect(clip, v);
                    half.x = (clip.z - clip.x) * 0.5f;
                    half.y = (clip.w - clip.y) * 0.5f;
                    if (half.x == 0 | half.y == 0)
                    {
                        return;
                    }
                    center.x = half.x + clip.x;
                    center.y = half.y + clip.y;
                }
                var graphics = script as HGraphics;
                if (graphics != null)//计算图形
                {
                    if (graphics.Shadow)
                        AddShadow(graphics, buffer, ref q, ref scale, ref o, ref clip, ref center, ref half);
                    var vs = buffer.vertex;
                    var vc = vs.Count;
                    int dc = graphics.mesh.vertInfo.DataCount;
                    if (dc > 0)
                    {
                        float px = (0.5f - script.Pivot.x) * script.m_sizeDelta.x;
                        float py = (0.5f - script.Pivot.y) * script.m_sizeDelta.y;
                        Vector2 uv2 = Vector2.zero;
                        unsafe
                        {
                            HVertex* hv = graphics.mesh.vertInfo.Addr;
                            for (int j = 0; j < dc; j++)
                            {
                                var tp = hv[j].position;//局部顶点
                                tp.z = 0;
                                tp.x += px;
                                tp.y += py;
                                tp.x *= scale.x;
                                tp.y *= scale.y;
                                var t = q * tp;
                                t += o;
                                t.z = 0;
                                uv2.x = (t.x - center.x) / half.x * 0.001f + 0.5f;
                                uv2.y = (t.y - center.y) / half.y * 0.001f + 0.5f;
                                vs.Add(t);
                                buffer.colors.Add(hv[j].color);
                                buffer.uv.Add(hv[j].uv);
                                buffer.uv2.Add(uv2);
                                buffer.uv3.Add(hv[j].uv3);
                                buffer.uv4.Add(hv[j].uv4);
                            }
                        }
                        AddTris(graphics, buffer, vc, dc);
                    }
                }
            }

            int c = pipeLine[index].childCount;
            int os = pipeLine[index].childOffset;
            for (int i = 0; i < c; i++)
            {
                Batch(pipeLine, os, buffer, clip, center, half);
                os++;
            }
        }
        static void AddTris(HGraphics graphics, BatchBuffer buffer, int vc, int dc)
        {
            int max = vc + dc;
            if (graphics.mesh.tris != null && graphics.mesh.tris.Length > 0)
            {
                int tid = 0;
                var src = graphics.mesh.tris;
                int len = src.Length;
                if (len > 0)
                {
                    for (int k = 0; k < len; k++)
                    {
                        int index = TriBuffer[k] = src[k] + vc;
                        if (index >= max)
                            UnityEngine.Debug.LogError("数组超标");
                    }
                    buffer.MatCollector.CombinationMaterial(graphics, graphics.MainTexture, TriBuffer, len, ref tid);
                }
                AddUV1(buffer, tid, ref graphics.mesh.vertInfo);
            }
            else if (graphics.mesh.triangle.trisInfo.DataCount > 0 | graphics.mesh.triangle.trisInfo1.DataCount > 0 | graphics.mesh.triangle.trisInfo2.DataCount > 0)
            {
                Arrays[0].Length = 0;
                Arrays[1].Length = 0;
                Arrays[2].Length = 0;
                int s = 0;
                int l = 0;
                for (int i = 0; i < 4; i++)
                {
                    var mt = graphics.mesh.triangle[i];
                    var tc = mt.DataCount;
                    if (tc > 0)
                    {
                        Arrays[l].Start = s;
                        unsafe
                        {
                            int* ip = mt.Addr;
                            for (int k = 0; k < tc; k++)
                            {
                                TriBuffer[k] = ip[k] + vc;
                            }
                        }
                        Arrays[l].Length = tc;
                        s += tc;
                        l++;
                    }
                }
                buffer.MatCollector.CombinationMaterial(graphics, TriBuffer, Arrays, IDBuffer, l);
                unsafe
                {
                    HVertex* hv = graphics.mesh.vertInfo.Addr;
                    AddUV1(buffer, IDBuffer, hv, dc, l);
                }
            }
            else
            {
                AddUV1(buffer, 0, ref graphics.mesh.vertInfo);
            }
        }
        /// <summary>
        /// 添加阴影
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="buffer"></param>
        /// <param name="q"></param>
        /// <param name="scale"></param>
        /// <param name="o"></param>
        /// <param name="clip"></param>
        static void AddShadow(HGraphics graphics, BatchBuffer buffer, ref Quaternion q, ref Vector3 scale, ref Vector3 o, ref Vector4 clip, ref Vector2 center, ref Vector2 half)
        {
            var vs = buffer.vertex;
            var vc = vs.Count;
            int dc = graphics.mesh.vertInfo.DataCount;
            var os = graphics.shadowOffsset;
            if (dc > 0)
            {
                float px = (0.5f - graphics.Pivot.x) * graphics.SizeDelta.x + os.x;
                float py = (0.5f - graphics.Pivot.y) * graphics.SizeDelta.y + os.y;
                Vector2 uv2 = Vector2.zero;
                unsafe
                {
                    HVertex* vert = graphics.mesh.vertInfo.Addr;
                    for (int j = 0; j < dc; j++)
                    {
                        var tp = vert[j].position;//局部顶点
                        tp.x += px;
                        tp.y += py;
                        var t = q * tp;
                        t.x *= scale.x;
                        t.y *= scale.y;
                        t += o;
                        t.z = 0;
                        uv2.x = (t.x - center.x) / half.x * 0.001f + 0.5f;
                        uv2.y = (t.y - center.y) / half.y * 0.001f + 0.5f;
                        vs.Add(t);
                        buffer.colors.Add(graphics.shadowColor);
                        buffer.uv.Add(vert[j].uv);
                        buffer.uv2.Add(uv2);
                        buffer.uv3.Add(vert[j].uv3);
                        buffer.uv4.Add(vert[j].uv4);
                    }
                }
                AddTris(graphics, buffer, vc, dc);
            }
        }
        /// <summary>
        /// 添加UV
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="tid"></param>
        /// <param name="vertCount"></param>
        static void AddUV1(BatchBuffer buffer, int tid, ref MarshalBlock<HVertex> dat)
        {
            Vector2 v = Vector2.zero;
            v.x = tid;
            v.x *= 0.1f;
            var dc = dat.DataCount;
            unsafe
            {
                var hv = dat.Addr;
                for (int i = 0; i < dc; i++)
                {
                    v.y = hv[i].fillColor;
                    buffer.uv1.Add(v);
                }
            }
        }
        static Vector2[] UV1 = new Vector2[4];
        unsafe static void AddUV1(BatchBuffer buffer, int[] ids, HVertex* vertices, int vc, int l)
        {
            for (int i = 0; i < l; i++)
            {
                float x = ids[i];
                UV1[i].x = x * 0.1f;
                UV1[i].y = 0;
            }
            for (int i = 0; i < vc; i++)
            {
                int p = vertices[i].picture;
                if (p < 0 | p > 3)
                    UnityEngine.Debug.LogError("out of index");
                else
                {
                    UV1[p].y = vertices[i].fillColor;
                    buffer.uv1.Add(UV1[p]);
                }
            }

        }
        /// <summary>
        /// 矩形遮罩
        /// </summary>
        /// <param name="v0"></param>
        /// <param name="v1"></param>
        /// <returns></returns>
        static Vector4 CutRect(Vector4 v0, Vector4 v1)
        {
            if (v0.x < v1.x)
                v0.x = v1.x;
            if (v0.y < v1.y)
                v0.y = v1.y;
            if (v0.z > v1.z)
                v0.z = v1.z;
            if (v0.w > v1.w)
                v0.w = v1.w;
            if (v0.x > v0.z)
            {
                v0.x = 0;
                v0.z = 0;
            }
            if (v0.y > v0.w)
            {
                v0.y = 0;
                v0.w = 0;
            }
            return v0;
        }
    }
}
